 /*******************************************************************************
  * Copyright (c) 2005, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.progress;

 import java.util.ArrayList ;
 import java.util.List ;

 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.ViewerComparator;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.ScrolledComposite;
 import org.eclipse.swt.events.ControlEvent;
 import org.eclipse.swt.events.ControlListener;
 import org.eclipse.swt.events.FocusAdapter;
 import org.eclipse.swt.events.FocusEvent;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.swt.widgets.Widget;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.IWorkbenchHelpContextIds;

 /**
  * The VirtualProgressViewer is a viewer that shows the details of all in
  * progress job or jobs that are finished awaiting user input. It only updates
  * those items that are currently visible.
  *
  * @since 3.3
  *
  */
 public class VirtualProgressViewer extends AbstractProgressViewer {

     Composite control;

     private ScrolledComposite scrolled;

     private Composite noEntryArea;

     /**
      * Create a new instance of the receiver with a control that is a child of
      * parent with style style.
      *
      * @param parent
      * @param style
      */
     public VirtualProgressViewer(Composite parent, int style) {
         scrolled = new ScrolledComposite(parent, SWT.V_SCROLL | style);
         int height = JFaceResources.getDefaultFont().getFontData()[0]
                 .getHeight();
         scrolled.getVerticalBar().setIncrement(height * 2);
         scrolled.setExpandHorizontal(true);
         scrolled.setExpandVertical(true);

         control = new Composite(scrolled, SWT.NONE);

         control.addControlListener(new ControlListener() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
              */
             public void controlMoved(ControlEvent e) {
                 updateVisibleItems();

             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
              */
             public void controlResized(ControlEvent e) {
                 updateVisibleItems();

             }
         });
         GridLayout layout = new GridLayout();
         layout.marginHeight = 0;
         layout.marginWidth = 0;
         control.setLayout(layout);
         control.setBackground(parent.getDisplay().getSystemColor(
                 SWT.COLOR_LIST_BACKGROUND));

         control.addFocusListener(new FocusAdapter() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.FocusAdapter#focusGained(org.eclipse.swt.events.FocusEvent)
              */
             public void focusGained(FocusEvent e) {
                 setFocus();
             }
         });

         PlatformUI.getWorkbench().getHelpSystem().setHelp(control,
                 IWorkbenchHelpContextIds.RESPONSIVE_UI);

         scrolled.setContent(control);
         hookControl(control);

         noEntryArea = new Composite(scrolled, SWT.NONE);
         noEntryArea.setLayout(new GridLayout());

         Text noEntryLabel = new Text(noEntryArea, SWT.SINGLE);
         noEntryLabel.setText(ProgressMessages.ProgressView_NoOperations);
         noEntryLabel.setBackground(noEntryArea.getDisplay().getSystemColor(
                 SWT.COLOR_WIDGET_BACKGROUND));
         GridData textData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
         noEntryLabel.setLayoutData(textData);
         noEntryLabel.setEditable(false);

         PlatformUI.getWorkbench().getHelpSystem().setHelp(noEntryLabel,
                 IWorkbenchHelpContextIds.RESPONSIVE_UI);

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.progress.AbstractProgressViewer#add(java.lang.Object[])
      */
     public void add(Object [] elements) {
         ViewerComparator sorter = getComparator();
         ArrayList newItems = new ArrayList (control.getChildren().length
                 + elements.length);

         Control[] existingChildren = control.getChildren();
         for (int i = 0; i < existingChildren.length; i++) {
             newItems.add(existingChildren[i].getData());
         }

         for (int i = 0; i < elements.length; i++) {
             newItems.add(elements[i]);
         }

         JobTreeElement[] infos = new JobTreeElement[newItems.size()];
         newItems.toArray(infos);

         if (sorter != null) {
             sorter.sort(this, infos);
         }

         // Update with the new elements to prevent flash
 for (int i = 0; i < existingChildren.length; i++) {
             ((VirtualInfoItem) existingChildren[i]).dispose();
         }

         for (int i = 0; i < newItems.size(); i++) {
             VirtualInfoItem item = createNewItem(infos[i]);
             item.setColor(i);
         }

         control.layout(true);
         updateForShowingProgress();
     }

     /**
      * Update for the progress being displayed.
      */
     private void updateForShowingProgress() {
         if (control.getChildren().length > 0) {
             scrolled.setContent(control);
         } else {
             scrolled.setContent(noEntryArea);
         }
     }

     /**
      * Create a new item for info.
      *
      * @param info
      * @return VirtualInfoItem
      */
     private VirtualInfoItem createNewItem(JobTreeElement info) {
         final VirtualInfoItem item = new VirtualInfoItem(control, SWT.NONE,
                 info);

         item.setIndexListener(new VirtualInfoItem.IndexListener() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.ui.internal.progress.VirtualInfoItem.IndexListener#selectNext()
              */
             public void selectNext() {
                 VirtualProgressViewer.this.selectNext(item);

             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.ui.internal.progress.VirtualInfoItem.IndexListener#selectPrevious()
              */
             public void selectPrevious() {
                 VirtualProgressViewer.this.selectPrevious(item);

             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.ui.internal.progress.VirtualInfoItem.IndexListener#select()
              */
             public void select() {

                 Control[] children = control.getChildren();
                 for (int i = 0; i < children.length; i++) {
                     VirtualInfoItem child = (VirtualInfoItem) children[i];
                     if (!item.equals(child)) {
                         child.selectWidgets(false);
                     }
                 }
                 item.selectWidgets(true);

             }
         });

         // Refresh to populate with the current tasks
 item.refresh();
         return item;
     }

     /**
      * Select the previous item in the receiver.
      *
      * @param item
      */
     protected void selectPrevious(VirtualInfoItem item) {
         Control[] children = control.getChildren();
         for (int i = 0; i < children.length; i++) {
             VirtualInfoItem child = (VirtualInfoItem) children[i];
             if (item.equals(child)) {
                 VirtualInfoItem previous;
                 if (i == 0) {
                     previous = (VirtualInfoItem) children[children.length - 1];
                 } else {
                     previous = (VirtualInfoItem) children[i - 1];
                 }

                 item.selectWidgets(false);
                 previous.selectWidgets(true);
                 return;
             }
         }
     }

     /**
      * Select the next item in the receiver.
      *
      * @param item
      */
     protected void selectNext(VirtualInfoItem item) {
         Control[] children = control.getChildren();
         for (int i = 0; i < children.length; i++) {
             VirtualInfoItem child = (VirtualInfoItem) children[i];
             if (item.equals(child)) {
                 VirtualInfoItem next;
                 if (i == children.length - 1) {
                     next = (VirtualInfoItem) children[0];
                 } else {
                     next = (VirtualInfoItem) children[i + 1];
                 }
                 item.selectWidgets(false);
                 next.selectWidgets(true);

                 return;
             }
         }

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
      */
     protected Widget doFindInputItem(Object element) {
         return null;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
      */
     protected Widget doFindItem(Object element) {
         Control[] existingChildren = control.getChildren();
         for (int i = 0; i < existingChildren.length; i++) {
             if (existingChildren[i].isDisposed()
                     || existingChildren[i].getData() == null) {
                 continue;
             }
             if (existingChildren[i].getData().equals(element)) {
                 return existingChildren[i];
             }
         }
         return null;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget,
      * java.lang.Object, boolean)
      */
     protected void doUpdateItem(Widget item, Object element, boolean fullMap) {
         if (usingElementMap()) {
             unmapElement(item);
         }
         item.dispose();
         add(new Object [] { element });
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.Viewer#getControl()
      */
     public Control getControl() {
         return scrolled;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
      */
     protected List getSelectionFromWidget() {
         return new ArrayList (0);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
      * java.lang.Object)
      */
     protected void inputChanged(Object input, Object oldInput) {
         super.inputChanged(input, oldInput);
         refreshAll();
         updateForShowingProgress();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
      */
     protected void internalRefresh(Object element) {
         if (element == null) {
             return;
         }

         if (element.equals(getRoot())) {
             refreshAll();
             return;
         }
         Widget widget = findItem(element);
         if (widget == null) {
             add(new Object [] { element });
             return;
         }
         ((VirtualInfoItem) widget).refresh();

         // Update the minimum size
 Point size = control.computeSize(SWT.DEFAULT, SWT.DEFAULT);
         size.x += IDialogConstants.HORIZONTAL_SPACING;
         size.y += IDialogConstants.VERTICAL_SPACING;

         scrolled.setMinSize(size);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.progress.AbstractProgressViewer#remove(java.lang.Object[])
      */
     public void remove(Object [] elements) {

         for (int i = 0; i < elements.length; i++) {

             // Make sure we are not keeping this one
 if (((JobTreeElement) elements[i]).isJobInfo()
                     && FinishedJobs.getInstance().isFinished(
                             (JobInfo) elements[i])) {
                 Widget item = doFindItem(elements[i]);
                 if (item != null) {
                     ((VirtualInfoItem) item).refresh();
                 }

             } else {
                 Widget item = doFindItem(elements[i]);
                 if (item != null) {
                     unmapElement(elements[i]);
                     item.dispose();
                 }
             }
         }

         Control[] existingChildren = control.getChildren();
         for (int i = 0; i < existingChildren.length; i++) {
             VirtualInfoItem item = (VirtualInfoItem) existingChildren[i];
             item.setColor(i);
         }
         control.layout(true);
         updateForShowingProgress();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
      */
     public void reveal(Object element) {

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List,
      * boolean)
      */
     protected void setSelectionToWidget(List l, boolean reveal) {

     }

     /**
      * Cancel the current selection
      *
      */
     public void cancelSelection() {

     }

     /**
      * Set focus on the current selection.
      *
      */
     public void setFocus() {
         Control[] children = control.getChildren();
         if (children.length > 0) {
             for (int i = 0; i < children.length; i++) {
                 VirtualInfoItem item = (VirtualInfoItem) children[i];
                 item.setButtonFocus();
                 return;
             }
         } else
             noEntryArea.setFocus();
     }

     /**
      * Refresh everything as the root is being refreshed.
      */
     private void refreshAll() {

         Object [] infos = getSortedChildren(getRoot());
         Control[] existingChildren = control.getChildren();

         for (int i = 0; i < existingChildren.length; i++) {
             existingChildren[i].dispose();

         }
         // Create new ones if required
 for (int i = 0; i < infos.length; i++) {
             VirtualInfoItem item = createNewItem((JobTreeElement) infos[i]);
             item.setColor(i);
         }

         control.layout(true);
         updateForShowingProgress();

     }

     /**
      * Set the virtual items to be visible or not depending on the displayed
      * area.
      */
     private void updateVisibleItems() {
         Control[] children = control.getChildren();
         int top = scrolled.getOrigin().y;
         int bottom = top + scrolled.getParent().getBounds().height;
         for (int i = 0; i < children.length; i++) {
             VirtualInfoItem item = (VirtualInfoItem) children[i];
             item.setDisplayed(top, bottom);

         }
     }

 }

